/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 spell_properties.cpp

	$Header: /heroes4/spell_properties.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "spell_properties.h"

#include <cassert>
#include <map>
#include "creature_type.h"
#include "creature_traits.h"
#include "elements_of.h"
#include "enum_map.h"
#include "enum_operations.h"
#include "spell.h"
#include "table.h"
#include "table_ptr.h"
#include "town_type.h"

namespace
{
	struct t_spell_properties
	{
		t_spell_properties();

		std::string		name;
		std::string		short_name;
		int				level;
		int				cost;
		int				base_power;
		int				power_increment;
		int				levels_for_increment;
		t_creature_type	summoned_creature;
		t_town_type		alignment;
		int				properties;
		std::string		help_text;
		std::string		flavor_text;
		std::string		mage_guild_text;
		std::string		spellbook_text;
		t_ai_spell_type	ai_type;
		int				ai_value;
	};

	t_spell_properties::t_spell_properties()
	{
		level = 1;
		cost = 0;
		base_power = 0;
		power_increment = 0;
		levels_for_increment = 0;
		summoned_creature = k_creature_none;
		alignment = k_town_life;
		properties = 0;
	};

	static t_char_ptr_pair const k_keywords[] = 
	{
		{ k_spell_acid,				"acid" },
		{ k_spell_aging,			"aging" },
		{ k_spell_animate_dead,		"animate dead" },
		{ k_spell_anti_magic,		"anti magic" },
		{ k_spell_armageddon,		"armageddon" },
		{ k_spell_banish,			"banish" },
		{ k_spell_berserk,			"berserk" },
		{ k_spell_bind_flyer,		"bind flyer" },
		{ k_spell_bind_wound,		"bind wound" },
		{ k_spell_binding,			"binding" },
		{ k_spell_bless,			"bless" },
		{ k_spell_blind,			"blind" },
		{ k_spell_bloodfrenzy,		"bloodfrenzy" },
		{ k_spell_bloodlust,		"bloodlust" },
		{ k_spell_burning_hands,	"burning hands" },
		{ k_spell_cancellation,		"cancellation" },
		{ k_spell_celestial_armor,	"celestial armor" },
		{ k_spell_chain_lightning,	"chain lightning" },
		{ k_spell_clone,			"clone" },
		{ k_spell_cloud_of_confusion,	"cloud of confusion" },
		{ k_spell_cold_resistance,		"cold resistance" },
		{ k_spell_confusion,			"confusion" },
		{ k_spell_cowardice,			"cowardice" },
		{ k_spell_curse,				"curse" },
		{ k_spell_death_call,			"death call" },
		{ k_spell_despair,				"despair" },
		{ k_spell_disintegrate,			"disintegrate" },
		{ k_spell_dispel,				"dispel" },
		{ k_spell_displacement,			"displacement" },
		{ k_spell_divine_aura,			"divine aura" },
		{ k_spell_divine_intervention,	"divine intervention" },
		{ k_spell_divine_protection,	"divine protection" },
		{ k_spell_divine_retribution,	"divine retribution" },
		{ k_spell_dragon_strength,		"dragon strength" },
		{ k_spell_evil_hour,			"evil hour" },
		{ k_spell_exorcism,				"exorcism" },
		{ k_spell_fear,					"fear" },
		{ k_spell_fear_effect,			"fear_effect" },
		{ k_spell_fire_aura_effect,		"fire_aura_effect" },
		{ k_spell_fire_bolt,			"fire bolt" },
		{ k_spell_fire_resistance,		"fire resistance" },
		{ k_spell_fire_ring,			"fire ring" },
		{ k_spell_fire_shield,			"fire shield" },
		{ k_spell_fireball,				"fireball" },
		{ k_spell_firebreathing,		"firebreathing" },
		{ k_spell_first_strike,			"first strike" },
		{ k_spell_flight,				"flight" },
		{ k_spell_forgetfulness,		"forgetfulness" },
		{ k_spell_fortune,				"fortune" },
		{ k_spell_freezing_attack,		"freeze" },
		{ k_spell_frozen,						"frozen" },
		{ k_spell_gas,						"choking gas" },
		{ k_spell_giant_strength,			"giant strength" },
		{ k_spell_greater_forgetfulness,	"greater forgetfulness" },
		{ k_spell_guardian_angel,			"guardian angel" },
		{ k_spell_hand_of_death,			"hand of death" },
		{ k_spell_haste,					"haste" },
		{ k_spell_heal,						"heal" },
		{ k_spell_health_boost,				"health boost" },
		{ k_spell_holy_armor,				"holy armor" },
		{ k_spell_holy_retribution,			"holy retribution" },
		{ k_spell_holy_water,				"holy water" },
		{ k_spell_holy_word,				"holy word" },
		{ k_spell_hypnotize,				"hypnotize" },
		{ k_spell_ice_bolt,					"ice bolt" },
		{ k_spell_illusionary_ally,			"illusionary ally" },
		{ k_spell_implosion,				"implosion" },
		{ k_spell_inferno,					"inferno" },
		{ k_spell_kreegan_fire,				"kreegan fire" },
		{ k_spell_life_drain,				"life drain" },
		{ k_spell_lightning,				"lightning" },
		{ k_spell_luck_boost,				"luck" },
		{ k_spell_magic_arrow,				"magic arrow" },
		{ k_spell_magic_fist,				"magic fist" },
		{ k_spell_magic_leech,				"magic leech" },
		{ k_spell_magic_mirror,				"magic mirror" },
		{ k_spell_magic_resistance,			"magic resistance" },
		{ k_spell_mana,						"mana" },
		{ k_spell_mana_leech,				"mana_leech" },
		{ k_spell_mana_flare,				"mana flare" },
		{ k_spell_martyr,					"martyr" },
		{ k_spell_mass_bless,				"mass bless" },
		{ k_spell_mass_cancellation,		"mass cancellation" },
		{ k_spell_mass_curse,				"mass curse" },
		{ k_spell_mass_dispel,				"mass dispel" },
		{ k_spell_mass_exorcism,			"mass exorcism" },
		{ k_spell_mass_first_strike,		"mass first strike" },
		{ k_spell_mass_fortune,				"mass fortune" },
		{ k_spell_mass_haste,				"mass haste" },
		{ k_spell_mass_healing,				"mass healing" },
		{ k_spell_mass_misfortune,			"mass misfortune" },
		{ k_spell_mass_precision,			"mass precision" },
		{ k_spell_mass_protection_from_chaos,	"mass protection from chaos" },
		{ k_spell_mass_protection_from_death,	"mass protection from death" },
		{ k_spell_mass_protection_from_nature,	"mass protection from nature" },
		{ k_spell_mass_protection_from_order,	"mass protection from order" },
		{ k_spell_mass_slayer,					"mass slayer" },
		{ k_spell_mass_slow,					"mass slow" },
		{ k_spell_mass_snake_strike,			"mass snake strike" },
		{ k_spell_mass_sorrow,					"mass sorrow" },
		{ k_spell_mass_spiritual_fervor,		"mass spiritual fervor" },
		{ k_spell_mass_weakness,				"mass weakness" },
		{ k_spell_mire,							"mire" },
		{ k_spell_misfortune,					"misfortune" },
		{ k_spell_morale_boost,					"morale" },
		{ k_spell_nature_haste,					"nature haste" },
		{ k_spell_order_slow,					"order slow" },
		{ k_spell_pain_mirror,					"pain mirror" },
		{ k_spell_panic,						"panic" },
		{ k_spell_pathfinding,					"pathfinding" },
		{ k_spell_blur,							"blur" },
		{ k_spell_plague,						"plague" },
		{ k_spell_poison,						"poison" },
		{ k_spell_poison_attack,				"poison attack" },
		{ k_spell_power_drain,					"power drain" },
		{ k_spell_prayer,						"prayer" },
		{ k_spell_precision,					"precision" },
		{ k_spell_immortality,					"immortality" },
		{ k_spell_protection_from_chaos,		"protection from chaos" },
		{ k_spell_protection_from_death,		"protection from death" },
		{ k_spell_protection_from_life,			"protection from life" },
		{ k_spell_protection_from_nature,		"protection from nature" },
		{ k_spell_protection_from_order,		"protection from order" },
		{ k_spell_quickness,					"quickness" },
		{ k_spell_quicksand,					"quicksand" },
		{ k_spell_raise_ghost,					"raise ghost" },
		{ k_spell_raise_skeletons,				"raise skeletons" },
		{ k_spell_raise_vampires,				"raise vampires" },
		{ k_spell_mass_blur,					"mass_blur" },
		{ k_spell_ray_of_suffering,				"ray of suffering" },
		{ k_spell_rebirth,						"rebirth" },
		{ k_spell_reflexes,						"reflexes" },
		{ k_spell_regeneration,					"regeneration" },
		{ k_spell_endurance,					"endurance" },
		{ k_spell_resurrection,					"resurrection" },
		{ k_spell_rock_growth,					"rock growth" },
		{ k_spell_sacred_ground,				"sacred ground" },
		{ k_spell_sacrifice,					"sacrifice" },
		{ k_spell_sanctuary,					"sanctuary" },
		{ k_spell_short_hypnotism,				"short hypnotism" },
		{ k_spell_simple_healing,				"simple healing" },
		{ k_spell_slayer,						"slayer" },
		{ k_spell_slow,							"slow" },
		{ k_spell_smoke,						"smoke" },
		{ k_spell_snake_strike,					"snake strike" },
		{ k_spell_song_of_peace,				"song of peace" },
		{ k_spell_sorrow,						"sorrow" },
		{ k_spell_speed,						"speed" },
		{ k_spell_spell_shackle,				"spell shackle" },
		{ k_spell_spiritual_fervor,				"spiritual fervor" },
		{ k_spell_steal_all_enchantments,		"steal all enchantments" },
		{ k_spell_steal_enchantment,			"steal enchantment" },
		{ k_spell_stone_gaze,					"stone_gaze" },
		{ k_spell_stoneskin,					"stoneskin" },
		{ k_spell_strength,						"strength" },
		{ k_spell_stun,							"stun" },
		{ k_spell_summon_elf,					"summon elf" },
		{ k_spell_summon_white_tiger,			"summon tiger" },
		{ k_spell_summon_air_elemental,			"summon air elemental" },
		{ k_spell_summon_boat,					"summon boat" },
		{ k_spell_summon_cerberi,				"summon cerberus" },
		{ k_spell_summon_demon,					"summon demon" },
		{ k_spell_summon_venom_spawn,			"summon venom spawn" },
		{ k_spell_summon_devils,				"summon devils" },
		{ k_spell_summon_earth_elemental,		"summon earth elemental" },
		{ k_spell_summon_faerie_dragon,			"summon faerie dragon" },
		{ k_spell_summon_fire_elemental,		"summon fire elemental" },
		{ k_spell_summon_griffin,				"summon griffin" },
		{ k_spell_summon_ice_demon,				"summon ice demon" },
		{ k_spell_summon_imps,					"summon imps" },
		{ k_spell_summon_leprechaun,			"summon leprechaun" },
		{ k_spell_summon_mantis,				"summon mantis" },
		{ k_spell_summon_phoenix,				"summon phoenix" },
		{ k_spell_summon_satyr,					"summon satyr" },
		{ k_spell_summon_sprite,				"summon sprite" },
		{ k_spell_summon_stinkbug,				"summon stinkbug" },
		{ k_spell_summon_waspwort,				"summon_waspwort" },
		{ k_spell_summon_unicorn,				"summon unicorn" },
		{ k_spell_summon_water_elemental,		"summon water elemental" },
		{ k_spell_summon_wolf,					"summon wolf" },
		{ k_spell_teleport,						"teleport" },
		{ k_spell_terrain_walk,					"terrain walk" },
		{ k_spell_terror,						"terror" },
		{ k_spell_town_gate,					"town gate" },
		{ k_spell_vampiric_touch,				"vampiric touch" },
		{ k_spell_visions,						"visions" },
		{ k_spell_water_walk,					"water walk" },
		{ k_spell_weakness,						"weakness" },

		{ k_spell_effect_berserk,				"berserk_effect" },
		{ k_spell_effect_block,					"block" },
		{ k_spell_effect_devour,				"devour" },
		{ k_spell_effect_guardian_robe,			"robe_of_the_guardian" },
		{ k_spell_effect_hypnotize,				"hypnotize_effect" },
		{ k_spell_effect_martyr_client,			"martyr_client" },
		{ k_spell_effect_sacrifice_client,		"sacrifice_client" },
		{ k_spell_effect_vampirism,             "vampirism" },

		{ k_spell_effect_potion_of_acid,			"acid_potion" },
		{ k_spell_effect_potion_of_binding,			"binding_potion" },
		{ k_spell_effect_potion_of_cold,			"cold_potion", },
		{ k_spell_effect_potion_of_despair,			"despair_potion" },
		{ k_spell_effect_potion_of_endurance,		"endurance_potion" },
		{ k_spell_effect_potion_of_fire,			"fire_potion" },
		{ k_spell_effect_potion_of_fire_resistance,	"fire_resistance_potion" },
		{ k_spell_effect_potion_of_gas,				"gas_potion" },
		{ k_spell_effect_potion_of_healing,			"healing_potion" },
		{ k_spell_effect_potion_of_health,			"health_potion" },
		{ k_spell_effect_potion_of_holy_water,		"holy_water_potion" },
		{ k_spell_effect_potion_of_luck,			"luck_potion" },
		{ k_spell_effect_potion_of_mana,			"mana_potion" },
		{ k_spell_effect_potion_of_mirth,			"mirth_potion" },
		{ k_spell_effect_potion_of_poison,			"poison_potion" },
		{ k_spell_effect_potion_of_precognition,	"precognition_potion" },
		{ k_spell_effect_potion_of_quickness,		"quickness_potion" },
		{ k_spell_effect_potion_of_resistance,		"resistance_potion" },
		{ k_spell_effect_potion_of_restoration,		"restoration_potion" },
		{ k_spell_effect_potion_of_smoke,			"smoke_potion" },
		{ k_spell_effect_potion_of_speed,			"speed_potion" },
		{ k_spell_effect_potion_of_strength,		"strength_potion" },

	};

	enum
	{
		k_keyword_column,
		k_name_column,
		k_short_name_column,
		k_level_column,
		k_cost_column,
		k_alignment_column,
		k_base_power_column,
		k_power_increase_column,
		k_power_per_level_column,
		k_creature_column,

		k_adventure_column,
		k_blessing_column,
		k_combat_column,
		k_curse_column,
		k_damage_column,
		k_summoning_column,

		k_targets_bodies_column,
		k_affects_elementals_column,
		k_affects_enemies_column,
		k_affects_friends_column,
		k_affects_mechanical_column,
		k_affects_undead_column,

		k_single_target_column,
		k_physical_column,
		k_cold_column,
		k_fear_column,
		k_fire_column,
		k_healing_column,
		k_line_of_sight_column,
		k_mind_column,
		k_resisted_column,
		k_teachable_column,

		k_help_column,
		k_flavor_text_column,
		k_mage_guild_text_column,
		k_spellbook_text_column,
		k_high_priority_column,

		k_ai_type_column,
		k_affects_defense_column,
		k_affects_magic_column,
		k_mass_effect_column,
		k_affects_offense_column,
		k_ai_value_column,

	};

	t_table_cache     g_spell_table( "spells" );
	typedef t_enum_map<t_spell> t_spell_map;

}; // unnamed namespace

static t_spell_map const& get_spell_map()
{
	static t_spell_map map( k_keywords, ELEMENTS_OF( k_keywords ), 
		                    k_spell_effect_count );

	return map;
}

// -----------------------------------------------------------------
// get type from string
// -----------------------------------------------------------------
static t_ai_spell_type get_ai_type( std::string const& name )
{
	if (name.empty())
		return k_ai_spell_type_none;

	static t_char_ptr_pair const keywords[] =
	{
		{ k_ai_spell_type_blessing,			"blessing" },
		{ k_ai_spell_type_curse,			"curse" },
		{ k_ai_spell_type_damage,			"damage" },
		{ k_ai_spell_type_hand_of_death,	"hand_of_death" },
		{ k_ai_spell_type_healing,			"healing" },
		{ k_ai_spell_type_resurrection,		"resurrection" },
		{ k_ai_spell_type_sacrifice,		"sacrifice" },
		{ k_ai_spell_type_summoning,		"summoning" },
	};

	static t_enum_map< t_ai_spell_type > const keyword_table( keywords, 
															  ELEMENTS_OF( keywords ),
															  k_ai_spell_type_count );

	return keyword_table[name];
}


// -----------------------------------------------------------------
// read properties from spreadsheet
// -----------------------------------------------------------------
t_spell_properties const& get_properties( t_spell spell )
{
	static t_spell_properties k_properties[k_spell_effect_count];
	static bool initialized = false;

	if (!initialized)
	{
		int const k_property_columns[][2] =
		{
			{ k_adventure_column,				k_spell_bit_adventure },
			{ k_affects_elementals_column,		k_spell_bit_affect_elemental },
			{ k_affects_enemies_column,			k_spell_bit_affect_enemy },
			{ k_affects_friends_column,			k_spell_bit_affect_friend },
			{ k_affects_mechanical_column,		k_spell_bit_affect_mechanical },
			{ k_affects_undead_column,			k_spell_bit_affect_undead },
			{ k_blessing_column,				k_spell_bit_blessing },
			{ k_cold_column,					k_spell_bit_cold },
			{ k_combat_column,					k_spell_bit_combat },
			{ k_curse_column,					k_spell_bit_curse },
			{ k_damage_column,					k_spell_bit_damage },
			{ k_fear_column,					k_spell_bit_fear },
			{ k_fire_column,					k_spell_bit_fire },
			{ k_healing_column,					k_spell_bit_healing },
			{ k_high_priority_column,			k_spell_bit_high_priority },
			{ k_line_of_sight_column,			k_spell_bit_line_of_sight },
			{ k_mind_column,					k_spell_bit_mind },
			{ k_physical_column,				k_spell_bit_physical },
			{ k_resisted_column,				k_spell_bit_resisted },
			{ k_single_target_column,			k_spell_bit_single_target },
			{ k_summoning_column,				k_spell_bit_summoning },
			{ k_targets_bodies_column,			k_spell_bit_targets_bodies },
			{ k_teachable_column,				k_spell_bit_teachable },
			{ k_affects_defense_column,			k_spell_bit_affects_defense },
			{ k_affects_magic_column,			k_spell_bit_affects_magic },
			{ k_mass_effect_column,				k_spell_bit_mass_effect },
			{ k_affects_offense_column,			k_spell_bit_affects_offense },
		};

		initialized = true;
		t_table_ptr			table = g_spell_table.get();
		std::string			keyword;
		t_table::iterator	line;
		t_spell             index;
		int					i;

		// skip header
		line = table->begin();
		line++;
		line++;
		for (; line != table->end(); line++)
		{
			if (line->size() == 0)
				continue;

			keyword = (*line)[k_keyword_column];
			if (keyword.empty())
				continue;
			index = get_spell_map()[keyword];
			if (index < 0)
				continue;
			assert( index >= 0 && index < k_spell_effect_count);

			t_spell_properties& properties = k_properties[index];

			properties.name = (*line)[k_name_column];
			properties.level = atoi((*line)[k_level_column].c_str()  );
			properties.cost  = atoi((*line)[k_cost_column].c_str() );
			properties.alignment = get_town_type( (*line)[k_alignment_column].c_str() );
			properties.base_power = atoi( (*line)[k_base_power_column].c_str() );
			properties.power_increment = atoi( (*line)[k_power_increase_column].c_str() );
			properties.levels_for_increment = atoi( (*line)[k_power_per_level_column].c_str() );
			properties.summoned_creature = get_creature( (*line)[k_creature_column].c_str() );
			assert( properties.summoned_creature != k_creature_none
				    || (*line)[k_creature_column].empty() );
			properties.properties = 0;
			for (i = 0; i < ELEMENTS_OF( k_property_columns ); i++)
			{
				if (!(*line)[k_property_columns[i][0]].empty())
					properties.properties |= k_property_columns[i][1];
			}
			if (properties.base_power > 0)
				properties.properties |= k_spell_bit_has_strength;

			properties.help_text = (*line)[k_help_column];
			properties.short_name = (*line)[k_short_name_column];
			properties.flavor_text = (*line)[k_flavor_text_column];
			properties.mage_guild_text	= (*line)[k_mage_guild_text_column];
			properties.spellbook_text	= (*line)[k_spellbook_text_column];
			properties.ai_type = get_ai_type( (*line)[k_ai_type_column] );
			properties.ai_value = atoi( (*line)[k_ai_value_column].c_str() );
		}

	}

	assert( spell >= 0 && spell < ELEMENTS_OF( k_properties ) );

	return k_properties[spell];
}

bool spell_is( t_spell spell, int spell_bits, bool all )
{
	if (all)
		return (get_properties( spell ).properties & spell_bits) == spell_bits;
	return (get_properties( spell ).properties & spell_bits) != 0;
}

const char* get_spell_help( t_spell spell )
{
	return get_properties( spell ).help_text.c_str();
}

t_town_type get_spell_alignment( t_spell spell )
{
	return get_properties( spell ).alignment;
}

int  get_spell_level( t_spell spell )
{
	return get_properties( spell ).level;
}

int  get_spell_cost( t_spell spell )
{
	return get_properties( spell ).cost;
}

const char* get_spell_keyword( t_spell spell )
{
	return get_spell_map()[spell];
}

t_spell     get_spell( std::string keyword )
{
	return get_spell_map()[keyword];
}


const char* get_spell_name( t_spell spell )
{
	return get_properties( spell ).name.c_str();
}

const char* get_short_spell_name( t_spell spell )
{
	return get_properties( spell ).short_name.c_str();
}

const char* get_spell_flavor_text( t_spell spell )
{
	return get_properties( spell ).flavor_text.c_str();
}

int get_basic_spell_power( t_spell spell, int level, int multiplier )
{
	t_spell_properties const& properties = get_properties( spell );
	int						  result;

	if (properties.power_increment == 0 || properties.levels_for_increment == 0)
		result = properties.base_power * multiplier / 100;
	else
		result = (properties.base_power * properties.levels_for_increment 
		          + properties.power_increment * level)
		         * multiplier / (properties.levels_for_increment * 100);
	if (is_summoning( spell ) && properties.summoned_creature != k_creature_none)
	{
		result = result / get_traits( properties.summoned_creature ).experience;
		if (result < 1)
			result = 1;
	}
	return result;
}

const char* get_mage_guild_text( t_spell spell )
{
	return get_properties( spell ).mage_guild_text.c_str();
}

const char* get_spellbook_text( t_spell spell )
{
	return get_properties( spell ).spellbook_text.c_str();
}

t_creature_type get_summoned_creature( t_spell spell )
{
	return get_properties( spell ).summoned_creature;
}

int get_mage_guild_spell_count( int spell_level, bool library )
{
	assert( spell_level >= 1 && spell_level <= 5 );

	static int const k_spell_counts[2][5] =
	{
		{ 3, 3, 2, 2, 1 },
		{ 2, 2, 2, 1, 1 },
	};

	return k_spell_counts[ library ][ spell_level - 1 ];
}

t_ai_spell_type get_ai_spell_type( t_spell spell )
{
	return get_properties( spell ).ai_type;
}

int	get_ai_spell_value( t_spell spell )
{
	return get_properties( spell ).ai_value;
}

t_spell translate_spell( t_spell spell )
{
	static t_spell	table[k_spell_count];
	static bool		initialized = false;

	if (!initialized)
	{
		t_spell index;

		for (index = t_spell(0); index < k_spell_count; enum_incr(index))
			table[index] = index;
		table[k_spell_immortality] = k_spell_guardian_angel;
		table[k_spell_luck_boost] = k_spell_fortune;
		table[k_spell_morale_boost] = k_spell_spiritual_fervor;
		initialized = true;
	}
	return table[spell];
}
